<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Java 文件生成器</title>
    <meta name="author" content="lhc & trae">
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            display: flex;
            gap: 20px;
            padding: 20px;
            background-color: #f0f2f5;
            height: 100vh;
            box-sizing: border-box;
            margin: 0;
        }
        .left-panel, .right-panel {
            flex: 1;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            display: flex;
            flex-direction: column;
        }
        .sql-input, #preview {
            flex: 1;
            display: flex;
            flex-direction: column;
        }
        textarea {
            flex: 1;
            margin: 10px 0;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 6px;
            resize: vertical;
            width: 97%;
        }
        button {
            padding: 12px 24px;
            background-color: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #1976D2;
        }
.tab-content {
    display: none;
    flex: 1;
    overflow-y: auto;
    white-space: pre-wrap;
    background: #f8f9fa;
    border-radius: 0 6px 6px 6px;
    padding: 15px;
    width: 97%;
}

.code {
    font-family: "Consolas", "Menlo", "Monaco", "Courier New", monospace;
    font-size: 14px; /* 推荐14-16px */
    line-height: 1.5; /* 行高提升可读性 */
    overflow-x: auto; /* 横向滚动条防止溢出 */
}

.tab-nav {
    display: flex;
    margin-bottom: -1px;
}

.tab-btn {
    padding: 12px 20px;
    background: #e9ecef;
    border: none;
    color: #6c757d;
    border-radius: 6px 6px 0 0;
    cursor: pointer;
    transition: all 0.3s;
}

.tab-btn.active {
    background: #2196F3;
    color: white;
    box-shadow: 0 2px 8px rgba(33,150,243,0.2);
    font-weight: 500;
}

.config-tabs {
    margin-bottom: 20px;
}

.config-tab-content {
    display: none;
    padding: 10px;
    border: 1px solid #e9ecef;
    border-radius: 0 6px 6px 6px;
    width: 97%;
}

.config-tab-content.active {
    display: block;
} /* 超出高度显示垂直滚动条 */
        
    </style>
</head>
<body>
    <div id="notification" style="display: none; position: fixed; top: 20px; left: 90%; transform: translateX(-50%); background: #4CAF50; color: white; padding: 12px 40px 12px 20px; border-radius: 4px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: opacity 0.3s;">
        <span id="notificationText" onclick="hideNotification()"></span>
        <!-- <button onclick="hideNotification()" style="position: absolute; right: 10px; top: 0%; transform: translateY(-50%); background: none; border: none; color: white; cursor: pointer;">X</button> -->
    </div>
    <div class="left-panel">
        <h2>配置输入</h2>
        <div class="config-tabs">
    <div class="tab-nav tab-nav-config" style="display: flex; margin-bottom: 0px;">
        <button class="tab-btn active" style="gap: 10px;" onclick="switchConfigTab('customInfo')">自定义信息</button>
        <button class="tab-btn" onclick="switchConfigTab('fieldConfig')">字段配置</button>
    </div>
    <div id="customInfo" class="config-tab-content active">
        <label for="author">作者:</label>
        <input type="text" id="author" placeholder="输入作者姓名" ><br>
        <label for="packageName">包名:</label>
        <input type="text" id="packageName" placeholder="输入包名"><br>
        <label for="ignorePrefix">忽略前缀:</label>
        <input type="text" id="ignorePrefix" placeholder="输入忽略前缀"><br>
        <label for="isAddLombok">是否添加Lombok（用于Model）:</label>
        <input type="checkbox" id="isAddLombok"><br>
        <label for="isAddSwagger">是否添加Swagger:</label>
        <input type="checkbox" id="isAddSwagger"><br>
        <label for="isSimpleXml">是否简易版XML:</label>
        <input type="checkbox" id="isSimpleXml"><br>
        <label for="isUseUUID">是否使用uuid作为主键（用于Xml）:</label>
        <input type="checkbox" id="isUseUUID"><br>
        <label for="successReturn">成功返回（用于Controller）:</label>
        <input type="text" id="successReturn" placeholder="成功返回"><br>
        <label for="failReturn">失败返回（用于Controller）:</label>
        <input type="text" id="failReturn" placeholder="失败返回">
    </div>
    <div id="fieldConfig" class="config-tab-content" style="display: none;">
        <!-- <div style="padding: 20px; color: #6c757d;">字段配置功能即将上线，敬请期待...</div> -->
        <p>请选择 SQL 类型查看对应的 Java 类型</p>
        <!-- 整型类 -->
        <label for="sqlType_bit">bit:</label>
        <select id="sqlType_bit">
            <option value="Boolean" selected>Boolean</option>
        </select>
        <label for="sqlType_boolean">boolean:</label>
        <select id="sqlType_boolean">
            <option value="Boolean" selected>Boolean</option>
        </select>
        <label for="sqlType_int">int:</label>
        <select id="sqlType_int">
            <option value="Integer" selected>Integer</option>
        </select>
        <label for="sqlType_tinyint">tinyint:</label>
        <select id="sqlType_tinyint">
            <option value="Integer" selected>Integer</option>
            <option value="Byte">Byte</option>
        </select>
        <label for="sqlType_bigint">bigint:</label>
        <select id="sqlType_bigint">
            <option value="Long" selected>Long</option>
        </select>
        <!-- 字符类 -->
        <br>
        <label for="sqlType_varchar">varchar:</label>
        <select id="sqlType_varchar">
            <option value="String" selected>String</option>
        </select>
        <label for="sqlType_text">text:</label>
        <select id="sqlType_text">
            <option value="String" selected>String</option>
        </select>
        <label for="sqlType_blob">blob:</label>
        <select id="sqlType_blob">
            <option value="String" selected>String</option>
        </select>
        <!-- 浮点类 -->
        <br>
        <label for="sqlType_float">float:</label>
        <select id="sqlType_float">
            <option value="Float" selected>Float</option>
        </select>
        <label for="sqlType_double">double:</label>
        <select id="sqlType_double">
            <option value="Double" selected>Double</option>
        </select>
        <label for="sqlType_decimal">decimal:</label>
        <select id="sqlType_decimal">
            <option value="BigDecimal" selected>BigDecimal</option>
        </select>
        <!-- 时间类 -->
        <br>
        <label for="sqlType_time">time:</label>
        <select id="sqlType_time">
            <option value="LocalTime" selected>LocalTime</option>
        </select>
        <label for="sqlType_date">date:</label>
        <select id="sqlType_date">
            <option value="LocalDate" selected>LocalDate</option>
            <option value="Date">Date</option>
        </select>
        <label for="sqlType_datetime">datetime:</label>
        <select id="sqlType_datetime">
            <option value="LocalDateTime" selected>LocalDateTime</option>
            <option value="Date">Date</option>
        </select>
        <label for="sqlType_timestamp">timestamp:</label>
        <select id="sqlType_timestamp">
            <option value="LocalDateTime" selected>LocalDateTime</option>
            <option value="Date">Date</option>
        </select>
    </div>
</div>
        <div class="sql-input">
            <div style="display: flex; align-items: center; gap: 10px;">
                <h3>输入建表 SQL</h3>
                <button style="padding: 8px 16px; background-color: #2196F3;" onclick="generateAndPreview()">生成</button>
            </div>
            <textarea id="sqlInput" class="code" placeholder="输入你的建表 SQL"></textarea>
        </div>
    </div>
    <div class="right-panel">
    <div style="display: flex; align-items: center; gap: 10px;">
        <h2>预览效果</h2>
        <button id="copyButton" style="padding: 8px 16px; background-color: #4CAF50;">复制</button>
    </div>
    <div class="tab-nav preview-tab-nav">
        <button class="tab-btn active" onclick="switchPreviewTab('model')">Model</button>
        <button class="tab-btn" onclick="switchPreviewTab('bo')">Bo</button>
        <button class="tab-btn" onclick="switchPreviewTab('xml')">Xml</button>
        <button class="tab-btn" onclick="switchPreviewTab('mapper')">Mapper</button>
        <button class="tab-btn" onclick="switchPreviewTab('service')">Service</button>
        <button class="tab-btn" onclick="switchPreviewTab('controller')">Controller</button>
    </div>
    <div class="preview-tab-content tab-content code" id="modelTab" style="display: block;"></div>
    <div class="preview-tab-content tab-content code" id="boTab" style="display: none;"></div>
    <div class="preview-tab-content tab-content code" id="xmlTab" style="display: none;"></div>
    <div class="preview-tab-content tab-content code" id="mapperTab" style="display: none;"></div>
    <div class="preview-tab-content tab-content code" id="serviceTab" style="display: none;"></div>
    <div class="preview-tab-content tab-content code" id="controllerTab" style="display: none;"></div>

    <script defer>
        // 统一配置对象
        const sqlConfig = {
            data: {
                // input
                author: 'jack',
                packageName: 'alibaba.com',
                ignorePrefix: 'sys_',
                successReturn: 'R.ok',
                failReturn: 'R.fail',
                // checkbox
                isAddLombok: true,
                isAddSwagger: true,
                isSimpleXml: true,
                isUseUUID: true,
                // select
                sqlType_tinyint: 'Integer',
                sqlType_bigint: 'Long',
                sqlType_varchar: 'String',
                sqlType_text: 'String',
                sqlType_blob: 'String',
                sqlType_float: 'Float',
                sqlType_double: 'Double',
                sqlType_decimal: 'BigDecimal',
                sqlType_time: 'LocalTime',
                sqlType_date: 'LocalDate',
                sqlType_datetime: 'LocalDateTime',
                sqlType_timestamp: 'LocalDateTime'
            },

            save(key, value) {
                this.data[key] = value;
                localStorage.setItem('sqlConfig', JSON.stringify(this.data));
            },

            load() {
                const config = localStorage.getItem('sqlConfig');
                if (config) {
                    this.data = JSON.parse(config);
                }
                Object.keys(this.data).forEach(key => {
                    const stored = localStorage.getItem(key);
                    if (stored !== null) this.data[key] = stored;
                });
                return this.data;
            }
        };

        // 初始化配置并绑定监听
        document.addEventListener('DOMContentLoaded', () => {
            const config = sqlConfig.load();
            Object.entries(config).forEach(([key, value]) => {
                const element = document.getElementById(key);
                if (element) {
                    element.type === 'checkbox' ? 
                        element.checked = value :
                        element.value = value;
                    
                    element.addEventListener('input', e => {
                        const saveValue = element.type === 'checkbox' ? e.target.checked : e.target.value;
                        sqlConfig.save(key, saveValue);
                    });
                    // // 区分事件类型
                    //  const eventType = element.type === 'checkbox' ? 'change' : 'input';
                    //  element.addEventListener(eventType, e => {
                    //      const saveValue = element.type === 'checkbox' ? e.target.checked : e.target.value;
                    //      sqlConfig.save(key, saveValue);
                    //      // 强制更新内存中的值
                    //      sqlConfig.data[key] = saveValue;
                    //  });
                }
            });
        });
    </script>

    <script>
         // 页面初始化时将sqlInput初始化一个简单sql案例
        window.onload = function() {
            document.getElementById('sqlInput').value = `CREATE TABLE 'sys_user_info' (
  'user_id' int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
  'user_name' varchar(255) NOT NULL COMMENT '用户名',
  'status' tinyint(1) NOT NULL COMMENT '状态',
  'create_time' datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY ('user_id')
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'
`;
        }

         // 切换配置tab
        function switchConfigTab(tabName) {
            // 获取所有配置标签和内容面板
            const tabButtons = document.querySelectorAll('.config-tabs .tab-btn');
            const tabContents = document.querySelectorAll('.config-tab-content');
            
            // 移除所有活动状态
            tabButtons.forEach(btn => btn.classList.remove('active'));
            tabContents.forEach(content => {
                content.classList.remove('active');
                content.style.display = 'none';
            });
            
            // 设置选中状态
            document.querySelector(`.tab-btn[onclick*="${tabName}"]`).classList.add('active');
            document.getElementById(tabName).classList.add('active');
            document.getElementById(tabName).style.display = 'block';
        }

        // 切换previewTab
        function switchPreviewTab(tabName) {
            document.querySelectorAll('.preview-tab-content').forEach(tab => {
                tab.style.display = tab.id === tabName + 'Tab' ? 'block' : 'none';
            });
            document.querySelectorAll('.preview-tab-nav .tab-btn').forEach(btn => {
                btn.classList.toggle('active', btn.textContent.toLowerCase() === tabName);
            });
        }

    // 一键预览所有代码
    function generateAndPreview() {
        sqlInput = document.getElementById('sqlInput').value;
        const author = document.getElementById('author').value;
        const packageName = document.getElementById('packageName').value;
        const ignorePrefix = document.getElementById('ignorePrefix').value;

        if (!sqlInput) {
            return;
        }

        // 统一sql转换格式，用``且转小写（tip：有特定转换需求自行修改）
        sqlInput = sqlInput.replaceAll('\'', '`').toLowerCase(); 
        // fix：修复存在default 'xxx' 时，影响转换comment。直接先删掉
        sqlInput = sqlInput.replaceAll(/default\s+`(.*?)`/gi, ``);
        const tableNameMatch = sqlInput.match(/create table `(\w+)`/);
        if (!tableNameMatch) {
            return '无法解析表名';
        }
        // 表名
        let originTableName = tableNameMatch[1];
        let tableName = originTableName;
        if (ignorePrefix && tableName.startsWith(ignorePrefix)) {
            tableName = tableName.slice(ignorePrefix.length);
        }
        tableName = underscoreToCamel(tableName);
        // 表注释
        const tableCommentMatch = sqlInput.match(/comment\s*=\s*`([^`]*)`/i);
        let tableComment = '';
        if (tableCommentMatch) {
            tableComment = tableCommentMatch[1];
        }
        // 类名
        const className = tableName.charAt(0).toUpperCase() + tableName.slice(1);
        // 字段信息
        const fields = parseSqlFields(sqlInput, ignorePrefix);
        // 主键类型
        let primaryKey = getPrimaryKey(sqlInput, fields);
        
        // 1.解析 SQL 生成 Java 实体类
        const javaCode = generateModel(fields, author, packageName, ignorePrefix, className, originTableName, tableComment);
        document.getElementById('modelTab').textContent = javaCode;
        // 1.1 解析 SQL 生成实体类的 Bo 
        const boCode = generateBo(fields, author, packageName, ignorePrefix, className, originTableName, tableComment);
        document.getElementById('boTab').textContent = boCode;
        // 2.解析 SQL 生成 Xml
        const xmlCode = generateXml(fields, packageName, ignorePrefix, className, originTableName, primaryKey);
        document.getElementById('xmlTab').textContent = xmlCode;
        // 3.解析 SQL 生成 Mapper
        const mapperCode = generateMapper(author, packageName, ignorePrefix, className, primaryKey);
        document.getElementById('mapperTab').textContent = mapperCode;
        // 4.解析 SQL 生成 Service
        const serviceCode = generateService(author, packageName, ignorePrefix, className, primaryKey);
        document.getElementById('serviceTab').textContent = serviceCode;
        // 5.解析 SQL 生成 Controller
        const controllerCode = generateController(author, packageName, ignorePrefix, className, primaryKey);
        document.getElementById('controllerTab').textContent = controllerCode;

        // 初始化复制功能
        document.getElementById('copyButton').onclick = function() {
            let code =  Array.from(document.querySelectorAll('.preview-tab-content.tab-content[style*="display: block"]'))
                            .map(el => el.textContent);
            navigator.clipboard.writeText(code)
                .then(() => {
                document.getElementById('notificationText').textContent = '复制成功!';
                document.getElementById('notification').style.display = 'block';
                document.getElementById('notification').style.opacity = '1';
                setTimeout(hideNotification, 3000);
                // 定义 hideNotification 函数并设置 3 秒后调用
                function hideNotification() {
                    document.getElementById('notification').style.opacity = '0';
                    setTimeout(() => {
                        document.getElementById('notification').style.display = 'none';
                    }, 200);
                }
            })
                .catch(err => console.error('复制失败:', err));
            };
        }

        // 将sql转为需要的字段
        function parseSqlFields(sql, ignorePrefix) {
            // 字段匹配，直接获取
            const regex =  /`(?<originName>[^`]+)`\s+(?<type>\w+)[\s\S]*?`(?<comment>[^`]+)`/gi;
            const fields = [];
            let match;
            while ((match = regex.exec(sql)) !== null) {
                let type = convertSqlTypeToJava(match.groups.type);
                fields.push({
                    originName: match.groups.originName,
                    name: underscoreToCamel(match.groups.originName),     // 字段名
                    type: type,      // 字段类型
                    jdbcType: getJdbcType(match.groups.type), // jdbcType
                    comment: match.groups.comment    // 注释
                });
            }
            return fields;
        }

        // 获取主键信息
        function getPrimaryKey(sql, fields) {
            // 默认主键字段和类型
            let primaryKey = {originName: 'id', name: 'id', type: 'String'};
            // 简单匹配主键字段
            let primaryKeyRegex = /primary key\s*\(\s*`(\w+)`\s*\)/i;
            let match = sql.match(primaryKeyRegex);
            let primaryKeyOriginName = '';
            if (match) {
                primaryKeyOriginName = match[1];
            } else {
                return primaryKey;
            } 
            fields.forEach(f => {
                if (f.originName == primaryKeyOriginName) {
                    primaryKey.originName = f.originName;
                    primaryKey.name = f.name;
                    primaryKey.type = f.type;
                }
            })
            return primaryKey;
        }

        // 5.生成Controller代码
        function generateController(author, packageName, ignorePrefix, className, primaryKey) {
            // 变量名
            let cName = className.charAt(0).toLowerCase() + className.slice(1);
            // 返回信息
            let successReturn = document.getElementById('successReturn').value;
            let successReturnClassName = successReturn.substring(0, successReturn.lastIndexOf('.'));
            if (!successReturn) {
                successReturn = 'R.ok';
                successReturnClassName = 'R';
            }
            let failReturn = document.getElementById('failReturn').value;
            let failReturnClassName = failReturn.substring(0, failReturn.lastIndexOf('.'));
            if (!failReturn) {
                failReturn = 'R.fail';
                failReturnClassName = 'R';
            }
            // swagger
            let isAddSwagger = document.getElementById('isAddSwagger').checked;

            let controllerCode = `package ${packageName}.controller;

import ${packageName}.model.${className};
import ${packageName}.service.${className}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
`;
if (isAddSwagger) {
    controllerCode += 
`import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;\n`;
}

controllerCode += `\n/**\n`;
            if (author) {
                controllerCode += ` * @author ${author}\n`;
            }
            let datetimeStr = new Date().toLocaleString();
            controllerCode += ` * @date ${datetimeStr}\n */`;
            
if (isAddSwagger) {
    controllerCode += `\n@Api(tags = "${className}")`;
}
controllerCode +=
`
@RestController
@RequestMapping("/${cName}")
public class ${className}Controller {

    @Autowired
    private ${className}Service ${cName}Service;

    `+ (isAddSwagger ? `@ApiOperation("查询")` : ``) +`
    @GetMapping("/findOne/{${primaryKey.name}}")
    public ${failReturnClassName} findOne(@PathVariable("${primaryKey.name}")${primaryKey.type} ${primaryKey.name}) {
        return ${successReturn}(${cName}Service.findOne(${primaryKey.name}));
    }

    `+ (isAddSwagger ? `@ApiOperation("查询列表")` : ``) +`
    @PostMapping("/findList")
    public ${failReturnClassName} findList(@RequestBody ${className} model) {
        return ${successReturn}(${cName}Service.findList(model));
    }

    `+ (isAddSwagger ? `@ApiOperation("新增")` : ``) +`
    @PostMapping("/insert")
    public ${failReturnClassName} insert(@RequestBody ${className} model) {
        ${cName}Service.insert(model);
        return ${successReturn}();
    }

    `+ (isAddSwagger ? `@ApiOperation("更新")` : ``) +`
    @PostMapping("/update")
    public ${failReturnClassName} update(@RequestBody ${className} model) {
        ${cName}Service.update(model);
        return ${successReturn}();
    }

    `+ (isAddSwagger ? `@ApiOperation("删除")` : ``) +`
    @PostMapping("/delete/{${primaryKey.name}}")
    public ${failReturnClassName} delete(@PathVariable("${primaryKey.name}")${primaryKey.type} ${primaryKey.name}) {
        ${cName}Service.delete(${primaryKey.name});
        return ${successReturn}();
    }
    
}`

        return controllerCode;
        }

        // 4.生成Service代码
        function generateService(author, packageName, ignorePrefix, className, primaryKey) {
            // 判断是否轻量级xml
            let isSimpleXml = document.getElementById('isSimpleXml').checked;
            // 类名
            let cName = className.charAt(0).toLowerCase() + className.slice(1);
            let serviceCode = `package ${packageName}.service;

import ${packageName}.model.${className};
import ${packageName}.mapper.${className}Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
`;
serviceCode += `\n/**\n`;
            if (author) {
                serviceCode += ` * @author ${author}\n`;
            }
            let datetimeStr = new Date().toLocaleString();
            serviceCode += ` * @date ${datetimeStr}\n */`;
serviceCode +=
`
@Transactional
@Service
public class ${className}Service {

    @Autowired
    private ${className}Mapper ${cName}Mapper;

    public ${className} findOne(${primaryKey.type} ${primaryKey.name}) {
        return ${cName}Mapper.findOne(${primaryKey.name});
    }

    public List<${className}> findList(${className} model) {
        return ${cName}Mapper.findList(model);
    }

    public int insert(${className} model) {
        return ${cName}Mapper.insert(model);
    }
`;
serviceCode += isSimpleXml ? '' : `
    public int insertBatch(List<${className}> list) {
        return ${cName}Mapper.insertBatch(list);
    }
    
    public int insertSelective(${className} model) {
        return ${cName}Mapper.insertSelective(model);
    }
    `;
serviceCode += `    
    public int update(${className} model) {
        return ${cName}Mapper.update(model);
    }
    `;
serviceCode += isSimpleXml ? '' : `
    public int updateSelective(${className} model) {
        return ${cName}Mapper.updateSelective(model);
    }
    `;
serviceCode += `     
    public int delete(${primaryKey.type} ${primaryKey.name}) {
        return ${cName}Mapper.delete(${primaryKey.name});
    }
    `;
serviceCode += isSimpleXml ? '' : `
    public int deleteBatch(List<${primaryKey.type}> ${primaryKey.name}s) {
        return ${cName}Mapper.deleteBatch(${primaryKey.name}s);
    }
`;
serviceCode += 
`
}`
        return serviceCode;
    }

        // 3.生成Mapper代码
        function generateMapper(author, packageName, ignorePrefix, className, primaryKey) {
            let isSimpleXml = document.getElementById('isSimpleXml').checked;
            let mapperCode = 
`package ${packageName}.mapper;

import ${packageName}.model.${className};
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
`

mapperCode += `\n/**\n`;
            if (author) {
                mapperCode += ` * @author ${author}\n`;
            }
            let datetimeStr = new Date().toLocaleString();
            mapperCode += ` * @date ${datetimeStr}\n */`;

mapperCode += `
@Mapper
public interface ${className}Mapper {

    ${className} findOne(@Param("${primaryKey.name}") ${primaryKey.type} ${primaryKey.name});

    List<${className}> findList(${className} model);

    int insert(${className} model);
`;
mapperCode += isSimpleXml ? '' :
`
    int insertBatch(@Param("list") List<${className}> list);

    int insertSelective(${className} model);
`;
mapperCode +=
`
    int update(${className} model);
`;
mapperCode += isSimpleXml ? '' :
`
    int updateSelective(${className} model);
`;
mapperCode +=
`
    int delete(@Param("${primaryKey.name}") ${primaryKey.type} ${primaryKey.name});
`;
mapperCode += isSimpleXml ? '' :
`
    int deleteBatch(@Param("${primaryKey.name}s") List<${primaryKey.type}> ${primaryKey.name}s);
`
mapperCode +=`
}`

        return mapperCode;
    }

        // 2.生成xml代码
        function generateXml(fields, packageName, ignorePrefix, className, originTableName, primaryKey) {
            // 判断是否轻量级xml
            let isSimpleXml = document.getElementById('isSimpleXml').checked;
            // 判断是否使用uuid
            let isUseUUID = document.getElementById('isUseUUID').checked;
            // uuid生成代码片段 
            let uuidCode = !isUseUUID ? `` : `
        <selectKey resultType="java.lang.String" order="BEFORE" keyColumn="${primaryKey.originName}" keyProperty="${primaryKey.name}">
            SELECT UUID()
        </selectKey>`;

            // 构建MyBatis XML基础结构
            let xml = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${packageName}.mapper.${className}Mapper">

    <resultMap id="BaseResultMap" type="${packageName}.model.${className}">
`;
            
            // 添加字段映射
            fields.forEach(field => {
                xml += `        <result column="${field.originName}" property="${field.name}" jdbcType="${field.jdbcType}"/>
`;
            });
            
            // 添加基础SQL片段和CRUD操作
            xml += `    </resultMap>

    <sql id="Base_Column_List">
        ${fields.map(f => f.originName).join(', ')}
    </sql>
`;
xml +=
`    
    <select id="findOne" resultMap="BaseResultMap">
        SELECT
            <include refid="Base_Column_List"/>
        FROM ${originTableName}
        WHERE ${primaryKey.originName} = #{${primaryKey.name}}
    </select>

    <select id="findList" resultMap="BaseResultMap">
        SELECT
            <include refid="Base_Column_List"/>
        FROM ${originTableName}
        WHERE 1=1        ${fields.map(f => `
            <if test="${f.name} != null` + ifStringType(f.type, f.name) + `">
                and ${f.originName} = #{${f.name}}
            </if>`).join('')}
    </select>

    <insert id="insert" parameterType="${packageName}.model.${className}">${uuidCode}
        INSERT INTO ${originTableName}
            (<include refid="Base_Column_List"/>)
        VALUES
            (${fields.map(f => `#{${f.name}}`).join(',')})
    </insert>
`;
xml += isSimpleXml ? '' : `
    <insert id="insertSelective" parameterType="${packageName}.model.${className}">${uuidCode}
        INSERT INTO ${originTableName}
        <trim prefix="(" suffix=")" suffixOverrides=",">        ${fields.map(f => `
            <if test="${f.name} != null` + ifStringType(f.type, f.name) + `">
                ${f.originName},
            </if>`).join('')}
        </trim>
        VALUES
        <trim prefix="(" suffix=")" suffixOverrides=",">        ${fields.map(f => `
            <if test="${f.name} != null` + ifStringType(f.type, f.name) + `"> 
                #{${f.name}},
            </if>`).join('')}
        </trim>
    </insert>
`;
let insertBatchTempCode = '';
if (isUseUUID) {
    insertBatchTempCode += `uuid(),`;
    insertBatchTempCode += fields.filter(f => f.originName !== primaryKey.originName).map(f => `#{item.${f.name}}`).join(',');
} else {
    insertBatchTempCode += fields.map(f => `#{item.${f.name}}`).join(',');
}
xml += isSimpleXml ? '' : `
    <insert id="insertBatch">
        INSERT INTO ${originTableName}
            (<include refid="Base_Column_List"/>)
        VALUES
        <foreach collection="list" item="item" separator=",">
            (${insertBatchTempCode})
        </foreach>
    </insert>
`;
xml +=
`
    <update id="update" parameterType="${packageName}.model.${className}">
        UPDATE ${originTableName}
        <set>       ${fields.filter(f => f.originName !== primaryKey.originName).map(f => `\n           ${f.originName} = #{${f.name}}`).join(',')}
        </set>
        WHERE ${primaryKey.originName} = #{${primaryKey.name}}
    </update>
`; 
xml += isSimpleXml ? '' : `

    <update id="updateSelective" parameterType="${packageName}.model.${className}">
        UPDATE ${originTableName}
        <set>       ${fields.filter(f => f.originName !== primaryKey.originName).map(f => `
            <if test="${f.name} != null` + ifStringType(f.type, f.name) + `"> 
                ${f.originName} = #{${f.name}},
            </if>`).join('')}
        </set>
        WHERE ${primaryKey.originName} = #{${primaryKey.name}}
    </update>
`;
xml +=
`
    <delete id="delete">
        DELETE FROM ${originTableName}
        WHERE ${primaryKey.originName} = #{${primaryKey.name}}
    </delete>
`;
xml += isSimpleXml ? '' : `
    <delete id="deleteBatch" parameterType="java.util.List">
        DELETE FROM ${originTableName}
        WHERE ${primaryKey.originName} in (
            <foreach collection="${primaryKey.name}s" item="item" separator=",">
                #{item}
            </foreach>
        )
    </delete>
`;
xml +=
`
</mapper>`;
           
            return xml;
        }

        // 1.生成Model代码
        function generateModel(fields, author, packageName, ignorePrefix, className, originTableName, tableComment) {
            // 是否Lombok
            let isAddLombok = document.getElementById('isAddLombok').checked;
        
            let javaCode = '';
            // 拼接包名
            if (packageName) {
                javaCode += `package ${packageName}.model;\n\n`;
            }
            // 拼接import
            let toImportList = [];
            fields.map(f => {
            if (!toImportList.includes(f.type)) {
                toImportList.push(f.type);
            }});
            toImportList.forEach(field => {
                let importCode = getImportList(field);
                if (importCode) {
                    javaCode += `import ${importCode}; \n`;
                }
            });
            if (isAddLombok) {
                javaCode += `import lombok.Data;\n`;
            }


            // 拼接作者、时间
            javaCode += `\n/**\n`;
            javaCode += ` * ${tableComment} ${originTableName}\n`;
            if (author) {
                javaCode += ` * @author ${author}\n`;
            }
            let datetimeStr = new Date().toLocaleString();
            javaCode += ` * @date ${datetimeStr}\n */\n`;
            
            // 拼接类名
            if (isAddLombok) {
                javaCode += `@Data\n`;
            }
            javaCode += `public class ${className} {\n`;

            // 拼接字段
            fields.forEach(field => {
                if (field.comment ) {
                    javaCode += `    /**\n     * ${field.comment}\n     */\n`;
                }
                javaCode += `    private ${field.type} ${field.name};\n`;
            });
            javaCode += `\n`;
            
            if (!isAddLombok) {
                // 拼接get、set方法
                fields.forEach(field => {
                    const capitalizedName = field.name.charAt(0).toUpperCase() + field.name.slice(1);
                    javaCode += `    public ${field.type} get${capitalizedName}() {\n`;
                    javaCode += `        return ${field.name};
`;
                    javaCode += `    }\n\n`;
                    javaCode += `    public void set${capitalizedName}(${field.type} ${field.name}) {\n`;
                    javaCode += `        this.${field.name} = ${field.name};
`;
                    javaCode += `    }\n\n`;
                });
            }
            javaCode += `}`;

            return javaCode;
        }

        // 1.1 生成实体类的 Bo 
        function generateBo(fields, author, packageName, ignorePrefix, className, originTableName, tableComment) {
            // 是否Lombok
            let isAddLombok = document.getElementById('isAddLombok').checked;
            // 是否swagger
            let isAddSwagger = document.getElementById('isAddSwagger').checked;

            let boCode = '';
            // 拼接包名
            if (packageName) {
                boCode += `package ${packageName}.bo;\n\n`;
            }
            // 拼接import
            let toImportList = [];
            fields.map(f => {
            if (!toImportList.includes(f.type)) {
                toImportList.push(f.type);
            }});
            toImportList.forEach(field => {
                let importCode = getImportList(field);
                if (importCode) {
                    boCode += `import ${importCode}; \n`;
                }
            });
            if (isAddSwagger) {
                boCode += `import io.swagger.annotations.ApiModelProperty;\n`;
            }
            if (isAddLombok) {
                boCode += `import lombok.Data;\n`;
            }


            // 拼接作者、时间
            boCode += `\n/**\n`;
            if (author) {
                boCode += ` * @author ${author}\n`;
            }
            let datetimeStr = new Date().toLocaleString();
            boCode += ` * @date ${datetimeStr}\n */\n`;
            
            // 拼接类名
            if (isAddLombok) {
                boCode += `@Data\n`;
            }
            boCode += `public class ${className}Bo {\n`;

            // 拼接字段
            fields.forEach(field => {
                if (field.comment) {
                    if (isAddSwagger) {
                        boCode += `    @ApiModelProperty("${field.comment}")\n`;
                    } else {
                        boCode += `    /**\n     * ${field.comment}\n     */\n`;
                    }
                }
                boCode += `    private ${field.type} ${field.name};\n`;
            });
            boCode += `\n`;
            
            if (!isAddLombok) {
                // 拼接get、set方法
                fields.forEach(field => {
                    const capitalizedName = field.name.charAt(0).toUpperCase() + field.name.slice(1);
                    boCode += `    public ${field.type} get${capitalizedName}() {\n`;
                    boCode += `        return ${field.name};
`;
                    boCode += `    }\n\n`;
                    boCode += `    public void set${capitalizedName}(${field.type} ${field.name}) {\n`;
                    boCode += `        this.${field.name} = ${field.name};
`;
                    boCode += `    }\n\n`;
                });
            }
            boCode += `}`;

            return boCode;
        }
        
        // 字符串类型拼接时判断为空串
        function ifStringType(type, field) {
            if (type == 'String') {
                return ` and ${field} != ''`;
            }
            return ``;
        }

        // sql类型转为Java类型
        function convertSqlTypeToJava(sqlType) {
            sqlType = sqlType.toLowerCase();
            // if (sqlType.includes('int')) {
            //     return 'Integer';
            // } else if (sqlType.includes('bigint')) {
            //     return 'Long';
            // } else if (sqlType.includes('varchar') || sqlType.includes('text')) {
            //     return 'String';
            // } else if (sqlType.includes('decimal')) {
            //     return 'BigDecimal';
            // } else if (sqlType == 'time') {
            //     return 'LocalTime';
            // } else if (sqlType == 'date') {
            //     return 'LocalDate';
            // } else if (sqlType.includes('datetime') || sqlType.includes('timestamp')) {
            //     return 'LocalDateTime';
            // } else {
            //     return 'Object';
            // }
            // 直接通过select的值转换
            let javaType = document.getElementById(`sqlType_${sqlType}`).value;
            if (javaType) {
                return javaType;
            }
            return 'Object';
        }

        // sql类型转为标准JDBC类型
        function getJdbcType(sqlType) {
            const typeMap = {
                'int': 'INTEGER',
                'varchar': 'VARCHAR',
                'datetime': 'TIMESTAMP',
                'decimal': 'DECIMAL',
                'text': 'LONGVARCHAR',
                'bigint': 'BIGINT',
                'tinyint': 'TINYINT',
                'date': 'DATE',
                'time': 'TIME'
            };
            return typeMap[sqlType.toLowerCase()] || 'OTHER';
        }

        // 生成Java头顶的import
        function getImportList(javaType) {
            if (javaType == 'LocalDateTime') {
                return 'java.time.LocalDateTime';
            } else if (javaType == 'LocalDate') {
                return 'java.time.LocalDate';
            } else if (javaType == 'LocalTime') {
                return 'java.time.LocaTime';
            } else if (javaType == 'BigDecimal') {
                return 'java.math.BigDecimal';
            } else if (javaType == 'Date') {
                return 'java.util.Date';
            } else {
                return undefined;
            }
        }

        // 下划线转驼峰函数
        function underscoreToCamel(str) {
            return str.replace(/_([a-z])/g, (match, group) => group.toUpperCase());
        }
    </script>
</body>
</html>
